﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Access.PowerTools.Utilities;
using Access.PowerTools.Database.BackUp.Plug_In;
using Access.PowerTools.AbstractionLayer;
using Access.PowerTools.Database.Restore.Plug_In;
using System.Threading;
using SMSConsulting.SMSLIB.Utilities;

namespace Access.PowerTools.GUI
{
    public partial class BackupRestoreForm : Form
    {
        public BackupRestoreForm()
        {
            InitializeComponent();

            //if (Access.PowerTools.Settings.Globals.SimulationModeOn)
            //{
            //    Access.PowerTools.Settings.Globals.HostApplication =
            //        new Access.PowerTools.Simulation.Engine.SimulationCommunicationBridge();
            //}
        }

        private bool _backUp; // = true -> backup, else - restore
        private object _agent;
        //private BackUpAgent _agent;
        //private RestoreAgent _agent;
        private string _archiveFolder;
        private int _accessFormItemSuffix;

        public void Init(bool backUp, object agent, string archiveFolder)
        {
            _backUp = backUp;
            _agent = agent;
            _archiveFolder = archiveFolder;
            _accessFormItemSuffix = -1;

            if (_backUp) 
               (_agent as BackUpAgent).ReportProgressStatus += new EventHandler<RunProgressEventArgs>(ReportProgressStatus);
            else
                (_agent as RestoreAgent).ReportProgressStatus += new EventHandler<RunProgressEventArgs>(ReportProgressStatus);

            txtArchiveFolder.Text = archiveFolder;
            txtLog.Text = "";
 
            if (!System.IO.Directory.Exists(archiveFolder))
            {
                System.IO.Directory.CreateDirectory(archiveFolder);  
            }

            clearLog();
            listArchiveFolderFiles(archiveFolder); 

            if (_backUp)
            {
                chkCompactCurrentDb.Visible = false;
                lblFormItemSuffixToSet.Visible = false;
                txtFormItemSuffixToSet.Visible = false;

                chkClearBackUpFolder.Checked = false;

                this.Text = Access.PowerTools.Settings.Globals.MESSAGE_CAPTION +
                    " - Backup";
                cmdRun.Text = "&Run BackUp";

                grpObjectsToBackup.Text = "Select Objects to Backup";
            }
            else
            {
                chkCompactCurrentDb.Left = chkClearBackUpFolder.Left;
                chkCompactCurrentDb.Top = chkClearBackUpFolder.Top;
 
                chkClearBackUpFolder.Visible = false;
                chkCompactCurrentDb.Visible = true;

                this.Text = Access.PowerTools.Settings.Globals.MESSAGE_CAPTION +
                    " - Restore";
                cmdRun.Text = "&Run Restore";

                grpObjectsToBackup.Text = "Select Objects to Restore";
            }

            showDescription();
            listObjects(_backUp);            
        }

        private void listArchiveFolderFiles(string archiveFolder)
        {
            if (!System.IO.Directory.Exists(archiveFolder)) return;  

            string[] files = System.IO.Directory.GetFiles(archiveFolder);

            int count = 0;
            if (files != null) count = files.Length;

            StringBuilder sb = new StringBuilder(); 
            sb.AppendLine(string.Format("Archive Folder '{0}' has {1} files:",
                     archiveFolder, count));
            sb.AppendLine("");

            if (count > 0)
            {
                int index = 0;
                foreach (string file in files)
                {
                    index++;
                    sb.AppendLine(string.Format("{0:00}. {1}", index, file));
                }
            }

            addText(sb.ToString());  
        }

        private void clearLog()
        {
            txtLog.Text = "" ;
        }

        private void addText(string text)
        {
            txtLog.Text += System.Environment.NewLine + text;
        }

        private void listObjects(bool backupFlag)
        {
            if (backupFlag)
            {
                lstObjects.Items.Clear();

                BackUpAgent a = _agent as BackUpAgent;
                AccessApplication app = new AccessApplication();
                foreach (AccessDatabaseObjectInfo objectInfo in app.CurrentDatabase.AllObjects)
                {
                    lstObjects.Items.Add(objectInfo.ObjectTypeAndName);
                }
            }
            else
            {
                lstObjects.Items.Clear();

                if (!System.IO.Directory.Exists(_archiveFolder)) return;

                foreach (AccessDatabaseObjectInfo objectInfo in RestoreAgent.GetObjectsListFromArchiveFolder(_archiveFolder))   
                {
                    lstObjects.Items.Add(objectInfo.ObjectTypeAndName);
                }
            }
        }

        private void showDescription()
        {
            if (_backUp)
            {
                txtDescription.Text =
                    string.Format("[{0}] button will backup currently selected database objects into '{1}' folder", 
                      cmdRun.Text.Replace("&",""),
                      _archiveFolder);  
            }
            else
            {
                txtDescription.Text =
                    string.Format("[{0}] button will restore database objects from '{1}' folder files. "+
                                  "ATTENTION: existing objects will be replaced with their contents from source files.",
                      cmdRun.Text.Replace("&", ""),
                      _archiveFolder);
            }
        }

        private string  _currentOperationName;
        private bool _operationCancelled;
        private bool _operationFailed;
        private string _errorMessage;

        private List<AccessDatabaseObjectInfo> _selectedObjects; 
        private void cmdBackUp_Click(object sender, EventArgs e)
        {
            _operationCancelled = false;
            _operationFailed = false;
            _errorMessage = "";

            if (lstObjects.SelectedItems == null ||
                lstObjects.SelectedItems.Count <= 0)
            {
                Access.PowerTools.Utilities.Dialogs.ShowErrorMessage("There are no any objects selected to " + ((_backUp)?"Backup":"Restore") );
                return;
            }


            try
            {
                _selectedObjects = new List<AccessDatabaseObjectInfo>();
                foreach (string objectTypeAndName in lstObjects.SelectedItems)
                {
                    AccessDatabaseObjectInfo objectInfo = new AccessDatabaseObjectInfo(objectTypeAndName);
                    _selectedObjects.Add(objectInfo);
                }

                if (!_backUp && !System.IO.Directory.Exists(_archiveFolder))
                {
                    throw new ApplicationException(
                      string.Format("Archive folder doesn't exist", _archiveFolder)
                        );
                }

                if (!_backUp)
                {
                    if (!string.IsNullOrEmpty(txtFormItemSuffixToSet.Text))
                    {
                        int testInt;
                        if (!Int32.TryParse(txtFormItemSuffixToSet.Text, out testInt))
                        {
                            Access.PowerTools.Utilities.Dialogs.ShowWarningMessage(
                                string.Format("Non numeric value form MS Access Form ItemSuffix = '{0}'",
                                txtFormItemSuffixToSet.Text));
                            return;
                        }
                        if (testInt <= 0 || testInt > 2048)
                        {
                            Access.PowerTools.Utilities.Dialogs.ShowWarningMessage(
                                string.Format("MS Access Form ItemSuffix's value should be between 1 and 2048",
                                txtFormItemSuffixToSet.Text));
                            return;
                        }
                        _accessFormItemSuffix = testInt;
                    }
                }

                System.Threading.Thread.Sleep(100);

                _currentOperationName = "Backup";
                if (!_backUp) _currentOperationName = "Restore";
                started(_currentOperationName);

                txtLog.Text = "";

                cmdCancel.Visible = true;
                cmdCancel.Refresh();

                runBackupRestoreThread();

            }
            catch (ApplicationException ex)
            {
                cmdCancel.Visible = false; 
                this.Cursor = Cursors.Default;
                Access.PowerTools.Utilities.Dialogs.ShowErrorMessage(ex.Message);
            }
            catch (Exception ex)
            {
                cmdCancel.Visible = false;
                this.Cursor = Cursors.Default;
                Access.PowerTools.Utilities.Dialogs.ShowErrorMessage(ex.Message);
            }
        }

        private void backupRestoreFinished()
        {
            toolStripProgressBar1.Value = 0;

            finished(_currentOperationName, _operationCancelled, _operationFailed);

            cmdCancel.Visible = false;
            this.Cursor = Cursors.Default;
            this.Visible = true;

            if (_operationCancelled)
            {
                Access.PowerTools.Utilities.Dialogs.ShowWarningMessage(
                    string.Format("'{0}' cancelled.", _currentOperationName)
                    );
            }
            else if (_operationFailed)
            {
                Access.PowerTools.Utilities.Dialogs.ShowErrorMessage(
                    string.Format("'{0}' failed: {1}", _currentOperationName, _errorMessage)
                    );
            }
            else if (_backUp)
            {
                Access.PowerTools.Utilities.Dialogs.ShowInfoMessage(
                    string.Format("BackUp into '{0}' folder finished OK.", _archiveFolder)
                    );
            }
            else
            {
                Access.PowerTools.Utilities.Dialogs.ShowInfoMessage(
                    string.Format("Restore from '{0}' folder finished OK.", _archiveFolder)
                    );
            }
        }

        private void runBackupRestoreThread()
        {
            this.Cursor = Cursors.WaitCursor;

            BackgroundWorker backgroundWorker = new BackgroundWorker();
            backgroundWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
            backgroundWorker.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(AsyncWorkerFinishedExt);
            backgroundWorker.WorkerReportsProgress = true;
            backgroundWorker.WorkerSupportsCancellation = true;
            backgroundWorker.RunWorkerAsync(); //args);
        }

        private void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            backupRestore();
        }
        private void AsyncWorkerFinishedExt(object sender, RunWorkerCompletedEventArgs e)
        {
            backupRestoreFinished();
        }

        private void backupRestore()
        {
            AccessApplication app = new AccessApplication();
            try
            {
                if (_backUp)
                {
                    if (!System.IO.Directory.Exists(_archiveFolder))
                    {
                        System.IO.Directory.CreateDirectory(_archiveFolder);
                    }

                    if (chkClearBackUpFolder.Checked)
                    {
                        Access.PowerTools.Utilities.FilesAndFolders.ClearFolder(_archiveFolder);
                    }
                    BackUpAgent a = _agent as BackUpAgent;
                    //a.ReportProgressStatus += new EventHandler<RunProgressEventArgs>(ReportProgressStatus);
                    a.BackUpDatabase(_archiveFolder, _selectedObjects);
                    _operationCancelled = a.Cancelled;
                    _operationFailed = a.Failed;
                    _errorMessage = a.ErrorMessage; 

                }
                else
                {
                    reportLocalProgress("Starting current database restore operation", 1, 6);
                    if (Access.PowerTools.Settings.Globals.SimulationModeOn)
                    {
                        Access.PowerTools.Utilities.FilesAndFolders.ClearFolder(Access.PowerTools.Settings.Globals.RestoredFilesFolder);
                    }

                    RestoreAgent a = _agent as RestoreAgent;
                    //a.ReportProgressStatus += new EventHandler<RunProgressEventArgs>(ReportProgressStatus);

                    string currentDbFullPath = app.CurrentDatabase.FullPath;

                    // !!! you attempted to open a database
                    // which is already opened exclusively
                    // by user admin on machine ....

                    /////////////////////////////////////////////////////////////////
                    // doesn't work here - host application gets hanging -
                    // try to move all db file backup code:
                    // 1. closing 2. copying file 3. reopening current db
                    // into add-in's code
                    ////reportLocalProgress("Closing current database...", 1, 6);
                    ////this.Visible = false;
                    ////app.CloseCurrentDatabase();

                    //////app.Terminate(); 
                    ////app = null;
                    //////System.Threading.Thread.Sleep(1000);

                    ////this.Visible = true;
                    ////reportLocalProgress("Making backup of current database...", 2, 6);
                    ////Utilities.FilesAndFolders.BackUpFile(currentDbFullPath);

                    //////System.Threading.Thread.Sleep(500);
                    ////this.Visible = false;

                    ////app = new AccessApplication();

                    ////reportLocalProgress("Opening current database...", 3, 6);
                    ////app.OpenCurrentDatabase(currentDbFullPath);

                    //////System.Threading.Thread.Sleep(1000);

                    ////this.Visible = true;
                    ////////////////////////////////////////////////////////////////

                    reportLocalProgress("Restoring current database...", 4, 6);
                    a.RestoreDatabase(_archiveFolder, _selectedObjects, _accessFormItemSuffix);
                    _operationCancelled = a.Cancelled;
                    _operationFailed = a.Failed;
                    _errorMessage = a.ErrorMessage;

                    if (!_operationCancelled && !_operationFailed)
                    {
                        ////////////////////////////////////////////////////////////////
                        //System.Threading.Thread.Sleep(1000);
                        if (CompactDbAfterRestore)
                        {
                            reportLocalProgress("Compacting current database...", 5, 6);
                            this.Visible = false;
                            app.CompactCurrentDatabase();
                            ///////////////////////////////////////////////////////////////
                            reportLocalProgress("Database restore operation finished OK.", 6, 6);
                        }
                        //backupRestoreFinished();
                    }
                }
            }
            finally
            {
               //if (app != null) app.Terminate();
               app = null;
            }

        }

        private bool CompactDbAfterRestore
        {
            get
            {
                return chkCompactCurrentDb.Checked; 
            }
        }

        private void trace(string message)
        {
            string tracePref = "CompactDb: ";

            string logRecord = string.Format("{0} - {1}", tracePref, message);
            SimpleLogWriter.WriteLogLine(logRecord);
        }

        private void reportLocalProgress(string message, int c, int m)
        {
            RunProgressEventArgs e = new RunProgressEventArgs();
            e.Message = message;
            e.CurrentCountValue = c;
            e.MaxCountValue = m;
            ReportProgressStatus(this, e);

            trace(string.Format("[{0}/{1}] - {2}", c, m, message));
        }

        private void ReportProgressStatus(object sender, RunProgressEventArgs e)
        {
            if (txtLog.InvokeRequired)
            {
                object[] args = { sender, e };

                txtLog.Invoke(new ReportProgressStatusDelegate(ReportProgressStatusInvoke), args);
            }
            else
            {
                ReportProgressStatusInvoke(sender, e);
            }
        }

        private delegate void ReportProgressStatusDelegate(object sender, RunProgressEventArgs e);
        private void ReportProgressStatusInvoke(object sender, RunProgressEventArgs e)
        {
            string test = string.Format("AAA"); 

            toolStripProgressBar1.Minimum = 0;
            toolStripProgressBar1.Maximum = e.MaxCountValue;
            toolStripProgressBar1.Value = e.CurrentCountValue;

            txtLog.Text =  string.Format("{0:HH:mm:ss.fff}: ", DateTime.Now) +  
                           (e.Message + System.Environment.NewLine +
                           txtLog.Text);
            txtLog.Refresh();
  
            System.Threading.Thread.Sleep(10);   
        }

        private void started(string operationName)
        {
            RunProgressEventArgs e = new RunProgressEventArgs();
            e.CurrentCountValue = 0;
            e.MaxCountValue = 0;
            e.Message = operationName + " started.";
            ReportProgressStatus(this, e);
        }
        private void finished(string operationName, bool cancelled, bool failed)
        {
            RunProgressEventArgs e = new RunProgressEventArgs();
            e.CurrentCountValue = 0;
            e.MaxCountValue = 0;
            if (cancelled )
                e.Message = operationName + " cancelled.";
            else if (failed)
                e.Message = operationName + " failed: " + _errorMessage;
            else
                e.Message = operationName + " finished.";
            ReportProgressStatus(this, e);
        }
        //private void cmdRestore_Click(object sender, EventArgs e)
        //{
        //    txtLog.Text = "";
        //    System.Threading.Thread.Sleep(100);   

        //    string operationName = "Restore";
        //    started(operationName);
 
        //    string backupFolder =
        //        Access.PowerTools.Settings.Globals.ExportFilesFolder;

        //    string restoreFolder =
        //        Access.PowerTools.Settings.Globals.RestoredFilesFolder;

        //    FilesAndFolders.ClearFolder(restoreFolder);


        //    AccessApplication app = new AccessApplication();

        //    AccessDatabase database = app.CurrentDatabase;

        //    RestoreAgent a = new RestoreAgent(app);

        //    a.ReportProgressStatus += new EventHandler<RunProgressEventArgs>(ReportProgressStatus);    
        //    a.RestoreDatabase(backupFolder);

        //    finished(operationName);

        //}

        private void cmdClose_Click(object sender, EventArgs e)
        {
            try
            {
                this.Close();
            }
            catch (Exception ex)
            {
                Access.PowerTools.Utilities.Dialogs.ShowErrorMessage(ex.Message);   
            }
        }

        private void cmdSelectBackUpRestoreFolder_Click(object sender, EventArgs e)
        {
            if (_backUp)
                selectBackUpRestoreFolderDialog.Description = "Select BackUp Folder";
            else
                selectBackUpRestoreFolderDialog.Description = "Select Restore Folder";  

            if (System.IO.Directory.Exists(_archiveFolder))  
               selectBackUpRestoreFolderDialog.SelectedPath = _archiveFolder;
            else
               selectBackUpRestoreFolderDialog.RootFolder = Environment.SpecialFolder.MyDocuments;  

            selectBackUpRestoreFolderDialog.ShowNewFolderButton = true;
            
            DialogResult ret = selectBackUpRestoreFolderDialog.ShowDialog(this);

            if (ret == DialogResult.OK)
            {
                txtArchiveFolder.Text = selectBackUpRestoreFolderDialog.SelectedPath;
                archiveFolderChanged();
            }
        }

        private void archiveFolderChanged()
        {
            _archiveFolder = txtArchiveFolder.Text;

            clearLog();
            listArchiveFolderFiles(_archiveFolder);
            if (!_backUp) listObjects(_backUp);
            showDescription();
        }


        private void cmdCancel_Click(object sender, EventArgs e)
        {
            if (_backUp)
                (_agent as BackUpAgent).Cancel();
            else
                (_agent as RestoreAgent).Cancel();
            System.Threading.Thread.Sleep(10);   
        }

        private void lstObjects_DoubleClick(object sender, EventArgs e)
        {
            if (lstObjects.Items != null)
                for(int i = 0; i < lstObjects.Items.Count; i++)
                    lstObjects.SetSelected(i, true);    
        }

        private void txtArchiveFolder_TextChanged(object sender, EventArgs e)
        {
            archiveFolderChanged();
        }

    }
}
